package org.oc.qaq.aspect;

import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.oc.qaq.context.UserContext;
import org.oc.qaq.websocket.LogWebSocketHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.util.Arrays;

@Aspect
@Component
public class LogAspect {

    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    private final LogWebSocketHandler logWebSocketHandler;

    @Autowired
    public LogAspect(LogWebSocketHandler logWebSocketHandler) {
        this.logWebSocketHandler = logWebSocketHandler;
    }

    // 拦截 controller 包下的所有方法
    @Pointcut("execution(* org.oc.qaq.controller..*(..))")
    public void controllerMethods() {}


    // 拦截标记了 @Log 注解的方法
    @Pointcut("@annotation(org.oc.qaq.annotation.Log)")
    public void annotatedMethod() {}

    // 拦截标记了 @Log 注解的类中的所有方法
    @Pointcut("@within(org.oc.qaq.annotation.Log)")
    public void annotatedClass() {}

    // 切点
    @Pointcut("controllerMethods() || annotatedMethod() || annotatedClass()")
    public void loggablePoints() {}

    @Before("controllerMethods()")
    public void logBefore(JoinPoint joinPoint) {
        HttpServletRequest request = getCurrentHttpRequest();
        String ip = request != null ? request.getRemoteAddr() : "未知IP";
        String uri = request != null ? request.getRequestURI() : "未知路径";
        String userId = UserContext.getCurrentId();
        String username = getCurrentUsername();
        String methodName = joinPoint.getSignature().toShortString();
        String args = Arrays.toString(joinPoint.getArgs());
        String time = LocalDateTime.now().toString();

        String content = String.format("[开始] 用户: %s, ID: %s, IP: %s, 路径: %s, 方法: %s, 参数: %s, 时间: %s",
                username, userId, ip, uri, methodName, args, time);

        logger.info(content);
        writeLogToFileAndSend(content);
    }

    @AfterReturning(pointcut = "controllerMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().toShortString();
        String username = getCurrentUsername();
        String userId = UserContext.getCurrentId();
        String time = LocalDateTime.now().toString();

        String content = String.format("[成功] 用户: %s, ID: %s, 方法: %s, 返回值: %s, 时间: %s",
                username, userId, methodName, result, time);

        logger.info(content);
        writeLogToFileAndSend(content);
    }

    @AfterThrowing(pointcut = "controllerMethods()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().toShortString();
        String userId = UserContext.getCurrentId();
        String username = getCurrentUsername();
        String time = LocalDateTime.now().toString();

        String content = String.format("[异常] 用户: %s, ID: %s 方法: %s, 错误信息: %s, 时间: %s",
                username, userId, methodName, ex.getMessage(), time);

        logger.error(content);
        writeLogToFileAndSend(content);
    }

    private void writeLogToFileAndSend(String content) {
        try {
            Files.createDirectories(Paths.get("logs"));
            String logFilePath = "logs/app-log.log";
            Files.writeString(Paths.get(logFilePath),
                    content + System.lineSeparator(),
                    StandardOpenOption.CREATE, StandardOpenOption.APPEND);
            logWebSocketHandler.sendLog(content);
        } catch (IOException e) {
            logger.error("写入日志文件失败", e);
        }
    }

    private HttpServletRequest getCurrentHttpRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes != null ? attributes.getRequest() : null;
    }

    private String getCurrentUsername() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return (authentication != null && authentication.isAuthenticated()) ? authentication.getName() : "匿名用户";
    }
}
